//////////////////////////////////////////////////////////////////////////////
// Game.Events namespace
//
// Implements ability to bind multiplie handlers to single event of an object
// (Observer & Observable pattern)
namespace("Game.Events");

// EventRouter class. Implements linking multiple handlers to a single event.
Game.Events.EventRouter = function(obj, eventName)
{
    this.obj = obj;
    this.handlers = new Array();
    this.eventName = eventName;
    this.bindToObject();
}

Game.Events.EventRouter.prototype.bindToObject = function()
{
    var routers = Game.Events.getObjectEventRouters(this.obj);
    routers[this.eventName] = this;
    routers.length++;
    this.bindCallback();
}
            
Game.Events.EventRouter.prototype.bindCallback = function()
{
    // save old handler to keep it in chain
    var oldHandler = this.obj[this.eventName];
    var oThis = this;
    this.obj[this.eventName] = function(e)
    {
        var event = e || window.event;
        alert("xEvent: " + event);
        oThis.notify(event);
    };

    if (oldHandler)
        this.addHandler(oldHandler);
}

Game.Events.EventRouter.prototype.unbindFromObject = function()
{
    this.obj[this.eventName] = null;
    this.obj.eventRouters = null;
    this.obj = null;
}

Game.Events.EventRouter.prototype.unbindCallback = function()
{
    this.obj[this.eventName] = null;
    this.obj.eventRouters.pop(this.eventName);
    this.obj.eventRouters.length--;
    if (this.obj.eventRouters.length == 0)
        this.unbindFromObject();
}

Game.Events.EventRouter.prototype.addHandler = function(handler, isFirst)
{
    if (isFirst)
        this.handlers.unshift(handler);
    else
        this.handlers.push(handler);
}

Game.Events.EventRouter.prototype.removeHandler = function(handler)
{
    this.handlers.pop(handler);
    if (this.handlers.length == 0)
    {// no hanlders left for this event: unbind callback from the object
        this.unbindCallback();
    }
}

Game.Events.EventRouter.prototype.notify = function(e)
{
    forEach(this.handlers, function (src) { src(this, e); }, this );
}

//Game.Events.EventRouter.prototype.callback = function(event)
//{
//    var e = xEvent(event);
//    var router = this.eventRouter;
//    router.notify(e);
//}

Game.Events.getObjectEventRouters = function(obj)
{
    if (!obj.eventRouters)
        obj.eventRouters = new Array();
    return obj.eventRouters;
}

Game.Events.bind = function(obj, eventName, handler, isFirst)
{
    var routers = Game.Events.getObjectEventRouters(obj);
    var router = routers[eventName];
    if (!router)
        router = new Game.Events.EventRouter(obj, eventName);
    router.addHandler(handler, isFirst);
}